#region License Revision: 0 Last Revised: 6/18/2008 11:00 AM
/******************************************************************************
Copyright (c) Microsoft Corporation.  All rights reserved.


This file is licensed under the Microsoft Public License (Ms-PL). A copy of the Ms-PL should accompany this file. 
If it does not, you can obtain a copy from: 

http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
******************************************************************************/
#endregion // License
using System;
using System.Windows.Browser;
using System.Diagnostics;
using System.Collections.Generic;
using System.Globalization;
using System.Collections.ObjectModel;

namespace ScriptInterop
{
	/// <summary>
	/// JavaScript object wrapper in C#
	/// </summary>
	public class JSObject
	{
        #region Member Variables
        private ScriptObject scriptObject;
        #endregion // Member Variables

        #region Constructors
        /// <summary>
        /// Default constructor initializes object to null
        /// </summary>
        public JSObject(){ }

        /// <summary>
        /// Constructor takes ScriptObject that will be wrapped.
        /// </summary>
        /// <param name="soInit">ScriptObject coming in from script</param>
        public JSObject(ScriptObject scriptObject)
        {
            // Validate
            if (scriptObject == null) throw new ArgumentNullException("scriptObject");

            // Store
            ScriptObject = scriptObject;
        }
        #endregion // Constructors

        /// <summary>
        /// Called when the underlying ScriptObject has been assigned.
        /// </summary>
        /// <remarks>
        /// This override only happens once during the lifetime of the script object.
        /// </remarks>
        protected virtual void OnScriptObjectAssigned()
        {
        }

		/// <summary>
		/// Used to get the classname of an object, not used currently, but might be handy in the future. Derived classes provide own implementaion.
		/// </summary>
		public virtual string ClassName { get { return null; } }

        /// <summary>create a new instance of a class and return it</summary>
        /// <param name="parameters">A variable list of parameters.</param>
        /// <returns type="ScriptObject">The ScriptObject created </returns>		
        protected ScriptObject CreateClass(params object[] parameters)
        {
            string className = ClassName;
            if (string.IsNullOrEmpty(className)) { throw new InvalidOperationException(ExceptionStrings.ClassNameNotSet); }
            return HtmlPage.Window.CreateInstance(className, parameters);
        }
        
        /// <summary>
		/// GetHashCode() get the hash code for the object, just use the so hash code
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode()
		{
			return scriptObject.GetHashCode();
		}

		/// <summary>
		/// property to get the ScriptObject value
		/// </summary>
		public ScriptObject ScriptObject
		{
			get { return scriptObject; }
			set
            {
#if DEBUG
                if ((scriptObject != null) && (value != null))
                {
                    System.Diagnostics.Debug.WriteLine(ExceptionStrings.ScriptObjectAlreadySet);
                }
#endif //DEBUG                

                // Store
                scriptObject = value;

                // If we have a value we need to notify of availability.
                if (value != null)
                {
                    OnScriptObjectAssigned();
                }
            }
		}

		/// <summary>
		/// Cast any JSObject back to original ScriptObject 
		/// </summary>
		/// <param name="jso">JSObject we are casting back to a ScriptObject</param>
		/// <returns>original ScriptObject</returns>
        public static implicit operator ScriptObject(JSObject obj)
		{
			if (obj == null)
				return null;
			return obj.scriptObject; 
		}
		/// <summary>
		/// Calls a method (member function) on the Javascript Object
		/// </summary>
		/// <param name="name">name of the method</param>
		/// <param name="args">variable number of arguments</param>
		/// <returns>return value (if any)</returns>
		protected object Invoke(string name, params object[] args)
		{
			if (scriptObject == null)
				return null;
			return scriptObject.Invoke(name, args);
		}
		/// <summary>
		/// Gets a property value from a JavaScript Object
		/// </summary>
		/// <param name="name">name of the property</param>
		/// <returns>value of the property</returns>
		protected object GetProperty(string name)
		{
			if (scriptObject == null)
				return null;
			return scriptObject.GetProperty(name);
		}

		/// <summary>
		/// Sets a property on a JavaScript object
		/// </summary>
		/// <param name="name">property name</param>
		/// <param name="value">value</param>
		protected void SetProperty(string name, object value)
		{
			if (scriptObject == null)
				return;
			scriptObject.SetProperty(name, value);
		}

		/// <summary>
		/// Gets a property value from a JavaScript Object, returns default value of type T if null
		/// </summary>
		/// <typeparam name="T">Type of the property</typeparam>
		/// <param name="name">name of the property</param>
		/// <returns>value of the property</returns>
		protected T GetProperty<T>(string member)
		{
			object value = GetProperty(member);
            return ScriptObjectUtility.ConvertValue<T>(value);
		}

		/// <summary>
		/// Calls a method (member function) on the Javascript Object, Templated return value version
		/// </summary>
		/// <typeparam name="T">Type to use for a return value</typeparam>
		/// <param name="name">name of the method</param>
		/// <param name="args">variable number of arguments</param>
		/// <returns>return value of type 'T', returns default(T) for null </returns>
		protected T Invoke<T>(string name, params object[] args)
		{
			object value = Invoke(name, args);
            return ScriptObjectUtility.ConvertValue<T>(value);
		}

		/// <summary>
		/// Helper function to C# wrapper arrays to JavaScript ScriptObject arrays
		/// </summary>
		/// <param name="arrJSO">array of JSObjects</param>
		/// <returns>JavaScrip array containing the ScriptObject handles</returns>
		static protected JSArray<ScriptObject> ConvertToScriptObjectJSArray(JSObject[] array)
		{
			if (array == null)
				return null;
			int ilim = array.GetLength(0);
			JSArray<ScriptObject> arrSo = new JSArray<ScriptObject>(ilim);

			for (int i = 0; i < ilim; ++i)
			{
				arrSo[i] = (ScriptObject)array[i];
			}
			return arrSo;
		}

        static protected T[] ConvertToArray<T>(ScriptObject scriptObject) where T : JSObject, new()
        {
            if (scriptObject != null)
            {
                JSArray<ScriptObject> arrSo = new JSArray<ScriptObject>(scriptObject);
                return ConvertToArray<T>(arrSo);
            }
            else
            {
                return new T[0];
            }
        }

		static protected T[] ConvertToArray<T>(JSArray<ScriptObject> array) where T : JSObject, new()
		{
			if (array == null)
				return new T[0];
			T[] arrT = new T[array.Length];
			for (int i = 0; i < arrT.Length; ++i)
			{
				arrT[i] = new T();
				arrT[i].ScriptObject = array[i];
			}
			return arrT;
		}

        //static protected Collection<T> ConvertToCollection<T>(JSArray<ScriptObject> array) where T : JSObject, new()
        //{
        //    T[] arr = ConvertToArray<T>(array);
        //    return new Collection<T>(arr);
        //}

        //static protected ReadOnlyCollection<T> ConvertToReadOnlyCollection<T>(JSArray<ScriptObject> array) where T : JSObject, new()
        //{
        //    T[] arr = ConvertToArray<T>(array);
        //    return new ReadOnlyCollection<T>(arr);
        //}
    }
}
